package common;

import javafx.application.Application;
import javafx.application.Platform;
import javafx.scene.Scene;
import javafx.scene.SnapshotParameters;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.WritableImage;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import javafx.scene.text.Font;
import javafx.scene.paint.Color;

import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;

public class JavaGame extends Application {
    private static JavaGame instance;
    private Stage primaryStage;
    private static int initWidth = 800;
    private static int initHeight = 600;
    private static String initTitle = "Java Game";
    private static CountDownLatch instanceReady = new CountDownLatch(1);

    protected int width;
    protected int height;
    protected Canvas canvas;
    protected GraphicsContext gc;
    private WritableImage offscreenBuffer;
    private Canvas offscreenCanvas;
    private GraphicsContext offscreenGc;

    // 事件队列（线程安全，用锁保护）
    private final Queue<GameEvent> eventQueue = new LinkedList<>();
    private final Lock eventLock = new ReentrantLock();

    private TrayIcon trayIcon; // 系统托盘图标
    
    // 窗口拖动相关变量
    private double dragStartX = 0;
    private double dragStartY = 0;
    private boolean isDragging = false;
    private boolean windowDraggingEnabled = true; // 是否允许拖动窗口

    private long lastFrameTime = System.nanoTime();

    public JavaGame() {}

    // 新增：设置系统托盘图标（自定义图片）
    public void setTrayIcon(String iconPath, String tooltip) {
        if (!SystemTray.isSupported()) {
            System.out.println("系统不支持托盘图标");
            return;
        }

        // 校验文件是否存在
        File iconFile = new File(iconPath);
        if (!iconFile.exists()) {
            System.out.println("托盘图标文件不存在：" + iconPath);
            return;
        }

        try {
            BufferedImage image = ImageIO.read(iconFile);
            if (image == null) {
                System.out.println("无法读取图片文件（可能格式不支持）：" + iconPath);
                return;
            }
            trayIcon = new TrayIcon(image, tooltip);
            trayIcon.setImageAutoSize(true);
            SystemTray.getSystemTray().add(trayIcon);
        } catch (Exception e) {
            System.out.println("设置托盘图标失败：" + e.getMessage());
        }
    }

    // 新增：给托盘图标绑定点击事件（如显示/隐藏窗口）
    public void setTrayIconClickListener(Runnable onClick) {
        if (trayIcon != null) {
            trayIcon.addActionListener(e -> onClick.run());
        }
    }

    public void setWindowIcon(String iconPath) {
        if (primaryStage == null) return;
        // 提交到JavaFX主线程执行
        runOnFxThread(() -> {
            try {
                Image icon = new Image(new File(iconPath).toURI().toString());
                primaryStage.getIcons().add(icon);
            } catch (Exception e) {
                System.out.println("设置窗口图标失败：" + e.getMessage());
            }
        });
    }

    public static void startGame(Class<? extends JavaGame> gameClass, int width, int height, String title) {
        initWidth = width;
        initHeight = height;
        initTitle = title;
        instance = null;
        instanceReady = new CountDownLatch(1);
        
        // 在后台线程启动 JavaFX，避免阻塞主线程
        Thread fxThread = new Thread(() -> {
            launch(gameClass);
        });
        fxThread.setDaemon(true);
        fxThread.start();
        
        // 等待实例创建完成
        try {
            instanceReady.await();
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("等待游戏实例创建时被中断", e);
        }
    }

    public static void startGame(Class<? extends JavaGame> gameClass, int width, int height) {
        startGame(gameClass, width, height, "Java Game");
    }

    public void tick(int fps) {
        long currentTime = System.nanoTime();
        if( currentTime - lastFrameTime < 1000000000 / fps) {
            try {
                Thread.sleep(1000 / fps - (currentTime - lastFrameTime) / 1000000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        lastFrameTime = System.nanoTime();
    }

    @Override
    public void start(Stage primaryStage) {
        this.primaryStage = primaryStage;
        this.width = initWidth;
        this.height = initHeight;
        instance = this;

        // 屏幕画布（不变，用于最终显示）
        canvas = new Canvas(width, height);
        gc = canvas.getGraphicsContext2D();

        // 离屏Canvas（双缓冲核心，防频闪）
        offscreenCanvas = new Canvas(width, height);
        offscreenGc = offscreenCanvas.getGraphicsContext2D(); // 绘图上下文绑定到离屏Canvas
        offscreenBuffer = new WritableImage(width, height); // 缓冲图像用于暂存

        // 窗口设置（不变）
        primaryStage.initStyle(StageStyle.UNDECORATED);
        StackPane root = new StackPane(canvas);
        Scene scene = new Scene(root);
        primaryStage.setTitle(initTitle);
        primaryStage.setScene(scene);
        primaryStage.setResizable(false);

        init();
        bindEvents(scene);
        bindCanvasEvents(canvas);
        primaryStage.show();
        instanceReady.countDown();
    }

    // 初始化钩子
    public void init() {}

    public Image loadImage(String path, int width, int height) {
        return new Image(new File(path).toURI().toString(),
                width, height,
                false,  // 保持比例
                true
        );
    }

    public Image resizeImage(Image img, int width, int height) {
        if (img == null) {
            throw new IllegalArgumentException("图片不能为空");
        }

        ImageView imageView = new ImageView(img);
        imageView.setFitWidth(width);
        imageView.setFitHeight(height);
        imageView.setPreserveRatio(false);

        // 修正目标尺寸为 ImageView 的实际尺寸（避免计算误差）
        int actualWidth = (int) imageView.getBoundsInLocal().getWidth();
        int actualHeight = (int) imageView.getBoundsInLocal().getHeight();

        SnapshotParameters params = new SnapshotParameters();
        params.setFill(new Color(0, 0, 0, 0)); // 透明背景

        WritableImage resizedImage = new WritableImage(actualWidth, actualHeight);
        imageView.snapshot(params, resizedImage);

        return resizedImage;
    }

/**
 * 核心方法：将图片绘制到指定坐标并刷新显示
 * 该方法使用JavaFX的图形上下文将图像绘制到离屏缓冲区，并通过FX线程确保UI线程安全
 *
 * @param img 要绘制的图像对象
 * @param x 图像在画布上的x坐标位置
 * @param y 图像在画布上的y坐标位置
 */
    // 核心方法：将图片绘制到(x,y)并刷新
    public void blit(Image img, double x, double y) {
    // 使用runOnFxThread确保在JavaFX应用线程上执行绘制操作
        runOnFxThread(() -> offscreenGc.drawImage(img, x, y));
    }

    /**
     * 在指定位置绘制文本的方法
     * @param text 要绘制的文本内容
     * @param x 文本绘制的x坐标
     * @param y 文本绘制的y坐标
     * @param color 文本颜色
     * @param font 文本字体，如果为null则使用默认字体
     */
    public void drawText(String text, double x, double y, Color color, Font font) {
        // 处理空字体，使用默认字体
        if (font == null) {
            font = new Font("Arial",  12); // 默认字体：Arial 12号
        }
        // 抽取绘制文本逻辑，使用lambda表达式创建绘图任务
        Font finalFont = font;
        runOnFxThread(() -> {
            offscreenGc.setFill(color);
            offscreenGc.setFont(finalFont);
            offscreenGc.fillText(text, x, y + finalFont.getSize());
        });
    }

    /**
     * 绘制一个矩形的方法
     * @param x 矩形左上角的x坐标
     * @param y 矩形左上角的y坐标
     * @param w 矩形的宽度
     * @param h 矩形的高度
     * @param color 矩形的填充颜色
     */
    public void drawRect(double x, double y, double w, double h, Color color) {
        runOnFxThread(() -> {
            offscreenGc.setFill(color);
            offscreenGc.fillRect(x, y, w, h);
        });
    }

    /**
     * 绘制一个圆角矩形的方法
     * @param x 矩形左上角的x坐标
     * @param y 矩形左上角的y坐标
     * @param w 矩形的宽度
     * @param h 矩形的高度
     * @param radius 圆角半径
     * @param color 矩形的填充颜色
     */
    public void drawRoundedRect(double x, double y, double w, double h, double radius, Color color) {
        runOnFxThread(() -> {
            offscreenGc.setFill(color);
            offscreenGc.fillRoundRect(x, y, w, h, radius, radius);
        });
    }

    /**
     * 绘制一条线段的方法
     * @param x1 起始点x坐标
     * @param y1 起始点y坐标
     * @param x2 结束点x坐标
     * @param y2 结束点y坐标
     * @param color 线条颜色，如果为null则使用黑色
     * @param lineWidth 线条宽度，如果为0则使用1
     */
    public void drawLine(double x1, double y1, double x2, double y2, Color color, double lineWidth) {
    // 如果线宽为0，则设置为默认值1
        if (lineWidth == 0) {
            lineWidth = 1;
        }
    // 如果颜色为null，则设置为默认黑色
        if (color == null) {
            color = Color.BLACK;
        }
        Color finalColor = color;
        double finalLineWidth = lineWidth;
        runOnFxThread(() -> {
            offscreenGc.setStroke(finalColor);
            offscreenGc.setLineWidth(finalLineWidth);
            offscreenGc.strokeLine(x1, y1, x2, y2);
        });
    }

    /**
     * 绘制一个填充的圆形
     * @param x 圆心的x坐标
     * @param y 圆心的y坐标
     * @param radius 圆的半径
     * @param color 圆形的填充颜色
     */
    public void drawFillCircle(double x, double y, double radius, Color color) {
        runOnFxThread(() -> {
            offscreenGc.setFill(color);
            offscreenGc.fillOval(x - radius, y - radius, radius * 2, radius * 2);
        });
    }

    /**
     * 绘制一个空心圆形
     * @param x 圆心的x坐标
     * @param y 圆心的y坐标
     * @param radius 圆的半径
     * @param color 圆形的边框颜色，如果为null则使用黑色
     * @param lineWidth 圆形的边框宽度，如果为0则使用1
     */
    public void drawStrokeCircle(double x, double y, double radius, Color color, double lineWidth) {
        if (lineWidth == 0) {
            lineWidth = 1;
        }
        if (color == null) {
            color = Color.BLACK;
        }
        Color finalColor = color;
        double finalLineWidth = lineWidth;
        runOnFxThread(() -> {
            offscreenGc.setStroke(finalColor);
            offscreenGc.setLineWidth(finalLineWidth);
            offscreenGc.strokeOval(x - radius, y - radius, radius * 2, radius * 2);
        });
    }

    public void clearRect(int x, int y, int w, int h) {
        runOnFxThread(() -> offscreenGc.clearRect(x, y, w, h));
    }

    public void clear() {
        runOnFxThread(() -> {
            // 方案1：用完全透明色填充（推荐，保留Alpha通道）
            offscreenGc.setFill(new Color(0, 0, 0, 0));
            offscreenGc.fillRect(0, 0, width, height);
            // 方案2：clearRect（部分场景可能残留，建议叠加使用）
            offscreenGc.clearRect(0, 0, width, height);
        });
    }

    public void update() {
        runOnFxThread(() -> {
            // 将离屏缓冲的内容一次性绘制到屏幕画布
            gc.drawImage(offscreenCanvas.snapshot(null, offscreenBuffer), 0, 0);
        });
    }

    private void runOnFxThread(Runnable task) {
        if (Platform.isFxApplicationThread()) {
            task.run();
        } else {
            Platform.runLater(task);
        }
    }

    private void bindEvents(Scene scene) {
        // 键盘按下
        scene.setOnKeyPressed(e -> addEvent(new GameEvent(EventType.KEY_PRESS, e)));
        // 键盘释放
        scene.setOnKeyReleased(e -> addEvent(new GameEvent(EventType.KEY_RELEASE, e)));
        // 鼠标点击
        scene.setOnMouseClicked(e -> addEvent(new GameEvent(EventType.MOUSE_CLICK, e)));
        // 鼠标移动
        scene.setOnMouseMoved(e -> addEvent(new GameEvent(EventType.MOUSE_MOVE, e)));
        // 鼠标按下
        scene.setOnMousePressed(e -> {
            addEvent(new GameEvent(EventType.MOUSE_PRESS, e));
            // 记录拖动起始位置（使用屏幕坐标）
            if (primaryStage != null) {
                dragStartX = e.getScreenX() - primaryStage.getX();
                dragStartY = e.getScreenY() - primaryStage.getY();
                isDragging = true; // 始终设置拖动标志，以便检测拖动事件（即使窗口拖动被禁用）
            }
        });
        // 鼠标释放
        scene.setOnMouseReleased(e -> {
            addEvent(new GameEvent(EventType.MOUSE_RELEASE, e));
            isDragging = false;
        });
        
        // 鼠标拖动 - 实现窗口拖动功能，并将拖动事件添加到队列
        scene.setOnMouseDragged(e -> {
            // 将拖动事件添加到事件队列（无论是否启用窗口拖动）
            addEvent(new GameEvent(EventType.MOUSE_DRAG, e));
            // 实现窗口拖动功能（仅在启用时）
            if (primaryStage != null && isDragging && windowDraggingEnabled) {
                // 使用 Platform.runLater 确保不阻塞主线程
                Platform.runLater(() -> {
                    primaryStage.setX(e.getScreenX() - dragStartX);
                    primaryStage.setY(e.getScreenY() - dragStartY);
                });
            }
        });
        
        // 鼠标滚轮事件
        scene.setOnScroll(e -> {
            addEvent(new GameEvent(EventType.MOUSE_WHEEL, e));
        });
    }
    
    // 在Canvas上绑定事件，确保拖动事件能被正确检测
    private void bindCanvasEvents(Canvas canvas) {
        // 鼠标按下
        canvas.setOnMousePressed(e -> {
            if (primaryStage != null) {
                dragStartX = e.getScreenX() - primaryStage.getX();
                dragStartY = e.getScreenY() - primaryStage.getY();
                isDragging = true; // 始终设置拖动标志，以便检测拖动事件（即使窗口拖动被禁用）
            }
        });
        
        // 鼠标释放
        canvas.setOnMouseReleased(e -> {
            isDragging = false;
        });
        
        // 鼠标拖动 - 确保拖动事件能被检测，并将窗口拖动操作放到后台线程
        canvas.setOnMouseDragged(e -> {
            // 将拖动事件添加到事件队列（无论是否启用窗口拖动）
            addEvent(new GameEvent(EventType.MOUSE_DRAG, e));
            // 实现窗口拖动功能（仅在启用时）
            if (primaryStage != null && isDragging && windowDraggingEnabled) {
                // 使用 Platform.runLater 确保不阻塞主线程
                Platform.runLater(() -> {
                    primaryStage.setX(e.getScreenX() - dragStartX);
                    primaryStage.setY(e.getScreenY() - dragStartY);
                });
            }
        });
        
        // 鼠标移动时，如果按下了鼠标按钮，也触发拖动事件（作为备用）
        canvas.setOnMouseMoved(e -> {
            if (e.isPrimaryButtonDown() && isDragging) {
                // 将拖动事件添加到事件队列（无论是否启用窗口拖动）
                addEvent(new GameEvent(EventType.MOUSE_DRAG, e));
                // 实现窗口拖动功能（仅在启用时）
                if (primaryStage != null && windowDraggingEnabled) {
                    // 使用 Platform.runLater 确保不阻塞主线程
                    Platform.runLater(() -> {
                        primaryStage.setX(e.getScreenX() - dragStartX);
                        primaryStage.setY(e.getScreenY() - dragStartY);
                    });
                }
            }
        });
        
        // 鼠标滚轮事件
        canvas.setOnScroll(e -> {
            addEvent(new GameEvent(EventType.MOUSE_WHEEL, e));
        });
    }

    // 线程安全地添加事件到队列
    private void addEvent(GameEvent event) {
        eventLock.lock();
        try {
            eventQueue.add(event);
        } finally {
            eventLock.unlock();
        }
    }

    /**
     * 获取所有游戏事件的方法
     * @return 包含所有游戏事件的列表
     */
    public List<GameEvent> getEvents() {
        // 创建一个新的列表用于存储事件
        List<GameEvent> events = new ArrayList<>();
        // 获取事件锁以确保线程安全
        eventLock.lock();
        try {
            // 将队列中所有事件转移到列表，并清空队列
            while (!eventQueue.isEmpty()) {
                events.add(eventQueue.poll());
            }
        } finally {
            // 确保锁一定会被释放
            eventLock.unlock();
        }
        // 返回包含所有事件的列表
        return events;
    }

    // 停止游戏
    public void exit() {
        Platform.exit();
    }

    // 禁用窗口拖动功能
    public void disableWindowDragging() {
        windowDraggingEnabled = false;
        isDragging = false; // 停止当前拖动
    }
    
    // 启用窗口拖动功能
    public void enableWindowDragging() {
        windowDraggingEnabled = true;
    }
    
    // 检查窗口拖动是否启用
    public boolean isWindowDraggingEnabled() {
        return windowDraggingEnabled;
    }

    // Getter
    public int getWidth() { return width; }
    public int getHeight() { return height; }
    public static JavaGame getInstance() { return instance; }

    public boolean isShowing() {
        // 结果容器（lambda 中需用 final 变量）
        final boolean[] result = new boolean[1];
        // 使用 CountDownLatch 等待主线程执行结果
        CountDownLatch latch = new CountDownLatch(1);

        Platform.runLater(() -> {
            result[0] = primaryStage != null && primaryStage.isShowing();
            latch.countDown();
        });

        try {
            latch.await(); // 等待主线程返回结果
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            return false; // 中断时返回默认值
        }
        return result[0];
    }

    // 隐藏窗口（线程安全）
    public void hide() {
        runOnFxThread(() -> {
            if (primaryStage != null) {
                primaryStage.hide();
            }
        });
    }

    // 显示窗口（线程安全）
    public void show() {
        runOnFxThread(() -> {
            if (primaryStage != null) {
                primaryStage.show();
            }
        });
    }
}